﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using SlimDX;
using SlimDX.Direct3D9;

namespace Editor.Rendering
{
    public class Material : IComparable
    {
        private Shader m_Shader;
        private String m_TextureFileName;
        private Texture m_DiffuseTexture;
        private EffectHandle m_Technique;

        public Shader Shader
        {
            get { return m_Shader; }
        }

        public String TextureFileName
        {
            get { return m_TextureFileName; }
            set { m_TextureFileName = value; }
        }

        public EffectHandle Technique
        {
            get { return m_Technique; }
            set { m_Technique = value; }
        }

        public Material(Device device, Shader shader) :
            this(device, shader, null)
        {
        }

        public Material(Device device, Shader shader, String textureFileName)
        {
            m_Shader = shader;
            m_TextureFileName = textureFileName;

            m_Technique = shader.Effect.FindNextValidTechnique(null);

            if (m_Shader.Parameters.ContainsKey("DiffuseTexture") && (!String.IsNullOrEmpty(textureFileName)))
            {
                m_DiffuseTexture = Texture.FromFile(device, textureFileName, Usage.None, Pool.Managed);
            }
        }

        public void Render(Device device, Matrix matWorld, Matrix matView, Matrix matProjection,
            VertexBuffer vertexBuffer, Type vertexType, int vertexCount,
            IndexBuffer indexBuffer, PrimitiveType primitiveType, int primitiveCount)
        {
            if (m_Shader.Parameters.ContainsKey("DiffuseTexture"))
            {
                if (m_DiffuseTexture != null)
                {
                    EffectHandle param = m_Shader.Parameters["DiffuseTexture"];
                    if (m_Shader.Effect.IsParameterUsed(param, m_Technique))
                    {
                        m_Shader.Effect.SetTexture(param, m_DiffuseTexture);
                    }
                }
            }

            m_Shader.ExecuteTechnique(device, m_Technique,
                matWorld, matView, matProjection,
                new Shader.PassRenderDelegate(RenderPass),
                vertexBuffer, vertexType, vertexCount,
                indexBuffer, primitiveType, primitiveCount);
        }

        private void RenderPass(Device device,
            VertexBuffer vertexBuffer, Type vertexType, int vertexCount,
            IndexBuffer indexBuffer, PrimitiveType primitiveType, int primitiveCount)
        {
            device.SetStreamSource(0, vertexBuffer, 0, Marshal.SizeOf(vertexType));
            device.VertexDeclaration = m_Shader.VertexDecl;

            if (indexBuffer != null)
            {
                device.Indices = indexBuffer;
                device.DrawIndexedPrimitives(primitiveType, 0, 0, vertexCount, 0, primitiveCount);
            }
            else
            {
                device.Indices = null;
                device.DrawPrimitives(primitiveType, 0, primitiveCount);
            }
        }

        public int CompareTo(Object obj)
        {
            Material other = obj as Material;
            int shaderRes = m_Shader.ShaderFileName.CompareTo(other.Shader.ShaderFileName);
            int textureRes = m_TextureFileName.CompareTo(other.TextureFileName);
            int techniqueRes = (m_Technique == other.Technique) ? 0 : 1;
            if (shaderRes != 0)
                return shaderRes;
            if (textureRes != 0)
                return techniqueRes;
            return techniqueRes;
        }
    }
}
